package com.xlh.service.courseTest.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.xlh.constant.FileSuffixConstant;
import com.xlh.constant.ReportConstant;
import com.xlh.dao.course.StepChapterMapper;
import com.xlh.dao.course.helper.ChapterMapperHelper;
import com.xlh.dao.courseTest.ExperimentReportMapper;
import com.xlh.dao.courseTest.ExperimentReportMapperExt;
import com.xlh.dao.courseTest.ExperimentStatisticsStepPassMapper;
import com.xlh.dao.courseTest.ext.ExperimentStatisticsStepPassMapperExt;
import com.xlh.dao.courseTest.helper.ExperimentReportMapperHelper;
import com.xlh.dao.courseTest.helper.ExperimentStatisticsPassMapperHelper;
import com.xlh.dao.courseTest.helper.ExperimentVideoStatisticsMapperHelper;
import com.xlh.dao.user.ext.UserClassMapperExt;
import com.xlh.dao.user.helper.UserClassMapperHelper;
import com.xlh.dto.ExperimentTimeDTO;
import com.xlh.dto.course.ChapterBasicInfoDTO;
import com.xlh.dto.course.CourseBasicDTO;
import com.xlh.dto.course.TemplateDataDTO;
import com.xlh.dto.courseTest.DuplicateResultDTO;
import com.xlh.dto.courseTest.ReportDTO;
import com.xlh.dto.courseTest.ReportNameDTO;
import com.xlh.dto.courseTest.UserPaperExperimentReportDTO;
import com.xlh.dto.courseTest.check.*;
import com.xlh.dto.courseTest.experiment.*;
import com.xlh.dto.user.UserInfoDTO;
import com.xlh.enums.LocalResourcesEnum;
import com.xlh.enums.course.ChapterEnum;
import com.xlh.enums.course.ChapterTypeEnum;
import com.xlh.enums.course.StepTypeEnum;
import com.xlh.enums.course.TemplateTypeEnum;
import com.xlh.enums.courseTest.ReportTypeEnum;
import com.xlh.exception.common.GlobalException;
import com.xlh.exception.common.NotFoundException;
import com.xlh.param.check.CheckSubmitParam;
import com.xlh.param.courseTest.ExperimentStatisticsAllParam;
import com.xlh.param.courseTest.ExperimentStatisticsParam;
import com.xlh.param.courseTest.ExperimentStatisticsTimeParam;
import com.xlh.param.courseTest.ReportParam;
import com.xlh.pojo.course.Chapter;
import com.xlh.pojo.course.ChapterBasic;
import com.xlh.pojo.course.StepChapter;
import com.xlh.pojo.course.StepChapterExample;
import com.xlh.pojo.courseTest.*;
import com.xlh.pojo.courseTest.example.ExperimentStatisticsStepPassExample;
import com.xlh.pojo.user.Class;
import com.xlh.pojo.user.User;
import com.xlh.pojo.user.UserClass;
import com.xlh.service.course.ChapterService;
import com.xlh.service.course.CourseService;
import com.xlh.service.course.TemplateService;
import com.xlh.service.courseTest.*;
import com.xlh.service.file.FileService;
import com.xlh.service.user.ClassService;
import com.xlh.service.user.UserService;
import com.xlh.util.*;
import com.xlh.virtualization.constant.ContextTypeEnum;
import com.xlh.virtualization.dao.JudgeMapper;
import com.xlh.virtualization.dao.helper.StatisticMapperHelper;
import com.xlh.virtualization.domain.Judge;
import com.xlh.virtualization.domain.Statistic;
import com.xlh.virtualization.domain.example.JudgeExample;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.CellStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author zhr 2019/4/3
 */
@Service
public class ExperimentReportServiceImpl implements ExperimentService, ExperimentReportService {

    private static Logger logger = LoggerFactory.getLogger(ExperimentReportServiceImpl.class);

    @Autowired
    private ExperimentReportMapper mapper;
    @Autowired
    private ExperimentReportMapperExt mapperExt;
    @Autowired
    private TemplateService templateService;
    @Autowired
    private CourseService courseService;
    @Autowired
    private CourseDuplicateService courseDuplicateService;
    @Autowired
    private AnswerSheetExperimentService answerSheetExperimentService;
    @Autowired
    private UserService userService;
    @Autowired
    private PaperQuestionService questionService;
    @Autowired
    private FileService fileService;
    @Autowired
    private UserClassService userClassService;
    @Autowired
    private UserClassMapperHelper userClassMapperHelper;
    @Autowired
    private ChapterService chapterService;
    @Autowired
    private ExperimentVideoStatisticsService videoStatisticsService;
    @Autowired
    private ExperimentStatisticsTaskPassService experimentStatisticsTaskPassService;
    @Autowired
    private ExperimentStatisticsStepPassMapper experimentStatisticsStepPassMapper;
    @Autowired
    private ExperimentStatisticsStepPassMapperExt experimentStatisticsStepPassMapperExt;
    @Autowired
    private JudgeMapper judgeMapper;
    @Autowired
    private StepChapterMapper stepChapterMapper;
    @Autowired
    private ExperimentStatisticsPassMapperHelper experimentStatisticsPassMapperHelperl;
    @Autowired
    private TheoryService theoryService;
    @Autowired
    private ClassService classService;
    @Autowired
    private ExperimentVideoStatisticsMapperHelper experimentVideoStatisticsMapperHelper;

    @Autowired
    private StatisticMapperHelper statisticMapperHelper;
    @Autowired
    private ChapterStatisticsExcelExportImpl chapterStatisticsExcelExportImpl;
    @Autowired
    private ChapterMapperHelper chapterMapperHelper;

    @Autowired
    private UserClassMapperExt userClassMapperExt;

    @Autowired
    private ExperimentReportMapperHelper experimentReportMapperHelper;


    @Override
    public ReportDTO getReport(Long courseId, Long chapterId, Long userId) {
        Long userClassId = userClassService.getUserClassId(courseId, userId);

        ExperimentReport report = selectByChapterIdUserId(chapterId, userClassId);

        // 实验报告为空时，异步插入一条新纪录
        if (report.getId() == null) {
            asyncInsert(chapterId, userClassId);
        }
        ReportDTO result = BeanUtil.transform(ReportDTO.class, report);
        if (result == null) {
            result = new ReportDTO();
        }

        return transformModelInReport(result, courseId, TemplateTypeEnum.COURSE_REPORT);
    }

    @Override
    public void deleteByChapterIds(List<Long> chapterIds) {
        ExperimentReportExample example = new ExperimentReportExample();
        example.createCriteria().andChapterIdIn(chapterIds);
        mapper.deleteByExample(example);
    }

    @Override
    public void deleteByStudentIdsChapterIds(List<Long> userIds, List<Long> chapterIds) {
        // todo 需要修改
        ExperimentReportExample example = new ExperimentReportExample();
        example.createCriteria().andStudentIdIn(userIds).andChapterIdIn(chapterIds);
        mapper.deleteByExample(example);
    }

    @Override
    public ExperimentReport selectByPrimaryKey(Long id) {
        return mapper.selectByPrimaryKey(id);
    }

    @Override
    public void updateByPrimaryKey(ExperimentReport experimentReport) {
        mapper.updateByPrimaryKey(experimentReport);
    }

    @Override
    public List<StudentListDTO> listStudent(Long classId, Long chapterId, Integer status, String studentInfo, Long userId) {
        List<StudentListDTO> list = mapperExt.listStudent(classId, chapterId, status, studentInfo);
        if (CollectionUtils.isNotEmpty(list)) {
            list.forEach(dto -> dto.setSubmit(dto.getId() != null));
        }
        return list;
    }

    @Override
    public List<ExperimentReport> selectByChapterIds(List<Long> chapterIds) {
        ExperimentReportExample example = new ExperimentReportExample();
        ExperimentReportExample.Criteria docSubmitCriteria = example.createCriteria()
                .andDocCommitEqualTo(true)
                .andChapterIdIn(chapterIds)
                .andDelMarkEqualTo(false);
        ExperimentReportExample.Criteria onlineSubmitCriteria = example.createCriteria()
                .andOnlineCommitEqualTo(true)
                .andChapterIdIn(chapterIds)
                .andDelMarkEqualTo(false);
        example.or(docSubmitCriteria);
        example.or(onlineSubmitCriteria);
        return mapper.selectByExample(example);
    }

    @Override
    public List<ExperimentReport> selectSubmitByChapterIdStudentIds(Long chapterId, List<Long> students) {
        ExperimentReportExample example = new ExperimentReportExample();
        ExperimentReportExample.Criteria docSubmitCriteria = example.createCriteria()
                .andDocCommitEqualTo(true)
                .andChapterIdEqualTo(chapterId)
                .andStudentIdIn(students)
                .andDelMarkEqualTo(false);
        ExperimentReportExample.Criteria onlineSubmitCriteria = example.createCriteria()
                .andOnlineCommitEqualTo(true)
                .andChapterIdEqualTo(chapterId)
                .andStudentIdIn(students)
                .andDelMarkEqualTo(false);
        example.or(docSubmitCriteria);
        example.or(onlineSubmitCriteria);
        return mapper.selectByExample(example);
    }

    @Override
    public void setPoint(ExperimentReportDTO result, Long id, List<ExperimentReport> reportList) {
        for (int i = 0; i < reportList.size(); i++) {
            ExperimentReport report = reportList.get(i);
            if (Objects.equals(id, report.getId())) {
                if (i + 1 < reportList.size()) {
                    result.setNext(reportList.get(i + 1).getId());
                }
                if (i - 1 >= 0) {
                    result.setPreview(reportList.get(i - 1).getId());
                }
                result.setId(id);
                break;
            }
        }
    }

    @Override
    public void setDetails(ExperimentReportDTO result, ExperimentReport report, List<ExperimentReport> reportList, Long courseId) {

        // 在线提交的时候进行展示
        if (report.getOnlineCommit()) {
            result.setReportContents(report.getReportHtml());
        }
        result.setReportName(report.getReportFileName());
        result.setReportAddress(LocalResourcesEnum.REPORT.getMappingUrl(report.getReportFile()));

        // 设置查重情况
        CourseDuplicate courseDuplicate = courseDuplicateService.getDuplicate(courseId);
        if (courseDuplicate != null) {
            result.setCheckReportDuplicate(courseDuplicate.getCheckReportDuplicate());
            result.setReportDuplicatePercent(courseDuplicate.getReportDuplicatePercent());
        } else {
            result.setCheckReportDuplicate(false);
            result.setReportDuplicatePercent(100);
        }

        if (result.getCheckReportDuplicate()) {
            result.setResultList(getDuplicateResult(reportList, report, result.getReportDuplicatePercent()));
        } else {
            result.setResultList(Lists.newArrayList());
        }

        result.setScore(report.getScore());
    }

    @Override
    @Transactional
    public String uploadReport(Long chapterId, Long courseId, MultipartFile file, Long userId, String username) {
        if (file == null) {
            throw new GlobalException("请上传实验报告");
        }
        String fileActualName = file.getOriginalFilename();

        Long userClassId = userClassService.getUserClassId(courseId, userId);
        if (userClassId == null) {
            throw new GlobalException("异常的用户-课程关系");
        }

        ExperimentReport report = selectByChapterIdUserId(chapterId, userClassId);
        if (report != null && report.getCheck()) {
            throw new GlobalException("报告已批阅,不可再次上传实验报告");
        }

        // 检查格式是否正确
        String extension = StrUtil.subAfter(fileActualName, ".", true);
        extension = StringUtils.isBlank(extension) ? null : extension.toLowerCase();
        if (extension == null || FileSuffixConstant.SUFFIX.contains(extension)) {
            throw new GlobalException("不支持无后缀、exe、sh文件格式上传");
        }
        extension = "." + extension;

        String fileName = String.format(ReportConstant.REPORT_FORMAT_NAME, username, extension);

        ReportNameDTO reportNameDTO = new ReportNameDTO(StringUtils.join(RandomUtil.simpleUUID(), extension), fileName);

        updateReport(chapterId, userClassId, reportNameDTO, null);

        asyncUpload(file, reportNameDTO.getFileServerName());

        return fileName;
    }

    @Override
    @Transactional
    public void saveReport(Long chapterId, Long courseId, ReportParam param, Long userId) {
        Long userClassId = userClassService.getUserClassId(courseId, userId);
        if (userClassId == null) {
            throw new GlobalException("异常的用户-课程关系");
        }

        ExperimentReport report = selectByChapterIdUserId(chapterId, userClassId);
        if (report != null && report.getCheck()) {
            throw new GlobalException("报告已批阅,不可再次上传实验报告");
        }

        updateReport(chapterId, userClassId, null, param);
    }

    @Override
    public void saveExperimentTime(Long taskId, Long courseId, Long classId, Long userId, Integer pass) {

        // 查询到记录则返回
        List<ExperimentStatisticsPass> passRecords = experimentStatisticsPassMapperHelperl
                .findExperimentStatisticsPassByCondition(taskId, courseId, classId, userId);
        if (CollectionUtil.isNotEmpty(passRecords)) return;

        // 根据任务获取章节ID
        List<ChapterBasicInfoDTO> taskBasicInfoDTOS = chapterService.listChapter(null, Lists.newArrayList(taskId), null, null);
        if (CollectionUtil.isEmpty(taskBasicInfoDTOS)) return;
        ChapterBasicInfoDTO taskInfo = taskBasicInfoDTOS.get(0);

        // 需要通过的实验判定
        List<Judge> judges = null;
        // 已经通过的实验判定
        List<ExperimentStatisticsStepPass> stepPasses = null;

        if (taskInfo.getType().equals(ChapterTypeEnum.THEORY_EXPERIMENT.getCode())
                && taskInfo.getStepType().equals(StepTypeEnum.MULTIPLE.getCode())) {
            // 获取所有实验步骤
            StepChapterExample example = new StepChapterExample();
            example.createCriteria().andChapterIdEqualTo(taskId);
            List<StepChapter> stepChapters = stepChapterMapper.selectByExample(example);
            judges = stepChapters.stream().map(allStep -> {
                Judge judge = new Judge();
                judge.setContextId(allStep.getId());
                judge.setType(Judge.STEP_TYPE);
                return judge;
            }).collect(Collectors.toList());
            stepPasses = experimentStatisticsStepPassMapperExt.selectStepByChapterId(Judge.STEP_TYPE, taskId, userId);
        } else {
            JudgeExample example = new JudgeExample();
            example.createCriteria().andContextIdEqualTo(taskId).andDeletedEqualTo(false).andTypeEqualTo(Judge.TASK_TYPE);
            judges = judgeMapper.selectByExample(example);
            ExperimentStatisticsStepPassExample isPassRecord = new ExperimentStatisticsStepPassExample();
            isPassRecord.createCriteria().andDeletedEqualTo(false).andUserIdEqualTo(userId.intValue()).andTypeEqualTo(Judge.TASK_TYPE).andContextIdEqualTo(taskId.intValue());
            stepPasses = experimentStatisticsStepPassMapper.selectByExample(isPassRecord);
        }
        // 所有步骤是否均已通过实验判定
        boolean isAllStepPass = judgesIsAllPass(judges, stepPasses);
        if (isAllStepPass) {

            mapper.insertExperimentTime(courseId, taskId, classId, userId, pass);
            // 获取所有任务
            List<ChapterBasicInfoDTO> allTask = chapterService.listTask(Lists.newArrayList(taskInfo.getParentId()), null);
            List<Integer> chapterIds = allTask.stream().map(task -> task.getId().intValue()).collect(Collectors.toList());
            final List<ExperimentStatisticsPass> list = experimentStatisticsPassMapperHelperl.findExperimentStatisticsPassByCondition(chapterIds, courseId, classId, userId);
            List<Integer> record = list.stream().map(passRecordNew -> passRecordNew.getChapterId().intValue()).collect(Collectors.toList());
            boolean isContainsAll = record.containsAll(chapterIds);
            // 所有任务均通过 表示章节通过
            if (isContainsAll) {
                List<ExperimentStatisticsPass> isPassRecords = experimentStatisticsPassMapperHelperl.findExperimentStatisticsPassByCondition(taskInfo.getParentId(), courseId, classId, userId);
                if (CollectionUtil.isEmpty(isPassRecords)) {
                    mapper.insertExperimentTime(courseId, taskInfo.getParentId(), classId, userId, pass);
                }
            }
        }
    }

    /**
     * 判定所有实验步骤已经通过
     *
     * @param judges
     * @param stepPasses
     * @return
     */
    private boolean judgesIsAllPass(List<Judge> judges, List<ExperimentStatisticsStepPass> stepPasses) {

        if (CollectionUtil.isEmpty(judges)) { // 没有实验判定
            return true;
        }
        Function<Judge, String> keyCreaterJud = judge -> judge.getContextId() + String.valueOf(judge.getType());
        Function<ExperimentStatisticsStepPass, String> keyCreaterPass = judge -> judge.getContextId() + String.valueOf(judge.getType());

        Set<String> allJudges = judges.stream().collect(Collectors.groupingBy(keyCreaterJud)).keySet();
        Set<String> allSteps = stepPasses.stream().collect(Collectors.groupingBy(keyCreaterPass)).keySet();

        return allSteps.containsAll(allJudges);
    }


    @Override
    public Integer queryExperiment(Long chapterId, Long courseId, Long classId, Long userId) {
        Integer queryExperimentById = mapper.findQueryExperimentById(courseId, chapterId, classId, userId);
        if (queryExperimentById == null) {
            queryExperimentById = 0;
        }
        return queryExperimentById;
    }

    @Override
    public List<Map<String, Object>> queryeExperimentTime(Long chapterId, Long courseId, Long classId, Long userId) {
        List<Map<String, Object>> listeall = Lists.newArrayList();
        HashMap maph = new HashMap();
        List<ExperimentTimeDTO> listex = Lists.newArrayList();
        List<UserInfoDTO> userInfoDTOS = userService.queryClassUser(Lists.newArrayList(classId), "");
        List<Long> userIds = userInfoDTOS.stream().map(UserInfoDTO::getId).collect(Collectors.toList());
        List<String> userNames = userInfoDTOS.stream().map(UserInfoDTO::getName).collect(Collectors.toList());

        List<Map<String, Object>> listStudyTime = mapper.queryExperimentStudyTime(userIds, chapterId);
        for (UserInfoDTO userInfoDTO : userInfoDTOS) {
            ExperimentTimeDTO experimentTimeDTO = new ExperimentTimeDTO();
            experimentTimeDTO.setUserName(userInfoDTO.getName());
            experimentTimeDTO.setUserId(userInfoDTO.getId());
            experimentTimeDTO.setUserNum(userInfoDTO.getUserNumber());
            for (Map<String, Object> map : listStudyTime) {
                if (map != null) {
                    String user_id = map.get("user_id") == null ? "" : map.get("user_id").toString();
                    if (user_id.equals(userInfoDTO.getId().toString())) {
                        BigDecimal b = new BigDecimal(Integer.valueOf(map.get("time").toString()) / 60);
                        experimentTimeDTO.setStudyTime(b.setScale(1, BigDecimal.ROUND_HALF_UP).toString());
                    }
                }
            }
            if ("".equals(experimentTimeDTO.getStudyTime()) || experimentTimeDTO.getStudyTime() == null) {
                experimentTimeDTO.setStudyTime("0");
            }
            listex.add(experimentTimeDTO);
        }
        List<Map<String, Object>> listVideoTime = mapper.queryExperimentVideoTime(classId, chapterId, courseId);
        Double sum = 0.0;
        Double sum1 = 0.0;
        for (ExperimentTimeDTO experimentTimeDTO : listex) {
            for (Map<String, Object> map : listVideoTime) {
                if (map != null) {
                    if (map.get("user_id").toString().equals(experimentTimeDTO.getUserId().toString())) {
                        BigDecimal b = new BigDecimal(Double.valueOf(map.get("sumTime").toString()) / 60);
                        experimentTimeDTO.setVideoTime(b.setScale(1, BigDecimal.ROUND_HALF_UP).toString());
                    }
                }
            }
            sum += Double.valueOf(experimentTimeDTO.getStudyTime() == null ? "0" : experimentTimeDTO.getStudyTime());
            sum1 += Double.valueOf(experimentTimeDTO.getVideoTime() == null ? "0" : experimentTimeDTO.getVideoTime());

            if ("".equals(experimentTimeDTO.getVideoTime()) || experimentTimeDTO.getVideoTime() == null) {
                experimentTimeDTO.setVideoTime("0");
            }
        }
        maph.put("studyAndVideoTime", listex);
        List<Map<String, Object>> experimentStatisticsPass = null;
        List listpass = Lists.newArrayList();
        PageInfo<Map<String, Object>> mapPageInfo = experimentStatisticsPass(chapterId, courseId, classId, userId, null, null);
        if (mapPageInfo == null) {
            throw new GlobalException("暂无学生");
        }
        if (mapPageInfo.getList().size() > 0) {
            for (Map<String, Object> map1 : mapPageInfo.getList()) {
                if (map1.get("id").toString().equals(chapterId.toString())) {
                    Map map = new HashMap();
                    String[] split = map1.get("passedName").toString().split(",");
                    List listS = Arrays.asList(split);
                    map.put("passedName", listS);
                    map.put("pass", map1.get("pass").toString());
                    map.put("passed", map1.get("passed").toString());
                    map.put("sum", userInfoDTOS.size());
                    BigDecimal b1 = new BigDecimal(map1.get("pass").toString());
                    BigDecimal b2 = new BigDecimal(userInfoDTOS.size());
                    BigDecimal b3 = new BigDecimal(b1.divide(b2, 4, BigDecimal.ROUND_HALF_UP).toString());
                    map.put("tgl", b3.multiply(new BigDecimal(100)).doubleValue());
                    listpass.add(map);
                }
            }
        }
        if (experimentStatisticsPass == null) {
            Map map = new HashMap();

            map.put("passedName", userNames);
            map.put("pass", 0);
            map.put("passed", userInfoDTOS.size());
            map.put("tgl", 0);
            map.put("sum", userInfoDTOS.size());
            listpass.add(map);
        }
        maph.put("pass", listpass);
        if (listex.size() == 0) {
            maph.put("avgStudy", 0);
            maph.put("avgVideo", 0);
        } else {
            BigDecimal b1 = new BigDecimal(sum);
            BigDecimal b2 = new BigDecimal(userInfoDTOS.size());
            BigDecimal b3 = new BigDecimal(sum1);
            maph.put("avgStudy", b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP).toString());
            maph.put("avgVideo", b3.divide(b2, 2, BigDecimal.ROUND_HALF_UP).toString());
        }
        listeall.add(maph);
        return listeall;
    }

    @Override
    public PageInfo<Map<String, Object>> experimentStatisticsPass(Long chapterId, Long courseId, Long classId, Long userId, Integer pageSize, Integer currentPage) {
        List<UserInfoDTO> userInfoDTOS = userService.queryClassUser(Lists.newArrayList(classId), "");
        if (userInfoDTOS.size() == 0) {
            PageInfo<Map<String, Object>> pageInfo = new PageInfo<>();
            pageInfo.setList(Lists.newArrayList());
            return pageInfo;
        }
        List<String> userNames = userInfoDTOS.stream().map(UserInfoDTO::getName).collect(Collectors.toList());
        List<Long> userIds = userInfoDTOS.stream().map(UserInfoDTO::getId).collect(Collectors.toList());

        if (pageSize != null) {
            PageHelper.startPage(currentPage, pageSize);
        }

        // 获取除理论课之外的所有课程类型
        List<ChapterTypeEnum> types = getConfigTypes();
        List<ChapterBasicInfoDTO> data = chapterService.listChapterByStepType(courseId, ChapterEnum.CHAPTER, null, types);
        List<String> listall2 = Lists.newArrayList();

        // 查询通过的学生
        List<Map<String, Object>> list = mapper.queryExperimentPass(courseId, classId, userIds);
        for (Map<String, Object> map : list) {
            listall2.add(map.get("id").toString());
        }

        List<Map<String, Object>> listall = Lists.newArrayList();
        List<Map<String, Object>> listall1 = Lists.newArrayList();
        for (ChapterBasicInfoDTO chapterBasicDTO : data) {
            if (listall2.contains(chapterBasicDTO.getId().toString())) {
                for (Map<String, Object> map : list) {
                    List<String> list1 = Lists.newArrayList();
                    List<String> list2 = Lists.newArrayList();
                    List<String> list3 = Lists.newArrayList();
                    List<String> list4 = Lists.newArrayList();
                    String passedName = map.get("passedName") == null ? "" : map.get("passedName").toString();
                    String passName = map.get("passName") == null ? "" : map.get("passName").toString();

                    String name = map.get("name").toString();
                    String id = map.get("id").toString();
                    if (id.equals(chapterBasicDTO.getId().toString())) {
                        String[] split = null;
                        String[] split1 = null;
                        if (!"".equals(passedName)) {
                            split = passedName.split(",");
                            list3 = Arrays.asList(split);
                            list4.addAll(list3);
                        }
                        if (!"".equals(passName)) {
                            split1 = passName.split(",");
                            list1 = Arrays.asList(split1);
                            list4.addAll(list1);
                        }
                        for (UserInfoDTO userInfoDTO : userInfoDTOS) {
                            if (!list4.contains(userInfoDTO.getName())) {
                                list2.add(userInfoDTO.getName());
                            }
                        }
                        String newStr = list2.stream().collect(Collectors.joining(","));
                        if (!"".equals(passedName)) {
                            map.put("passedName", map.get("passedName").toString() + "," + newStr);
                        } else {
                            if (!"".equals(newStr)) {
                                map.put("passedName", newStr);
                            } else {
                                map.put("passedName", "-");
                            }
                        }
                        if (!"".equals(passName)) {
                            map.put("passed", Integer.valueOf(map.get("passed").toString()) + list2.size());
                        } else {
                            map.put("passed", userInfoDTOS.size());

                        }
                        listall.add(map);
                    }
                }
            } else {
                Map<String, Object> mapno = new HashMap<>();
                mapno.put("name", chapterBasicDTO.getName());
                mapno.put("passed", userInfoDTOS.size());
                mapno.put("id", chapterBasicDTO.getId());
                mapno.put("pass", 0);
                mapno.put("passedName", userNames.stream().collect(Collectors.joining(",")));
                listall.add(mapno);
            }

        }
        List<ChapterBasicInfoDTO> data1 = chapterService.listChapterByStepType(courseId, ChapterEnum.CHAPTER, null, types);
        PageInfo<Map<String, Object>> pageInfo = new PageInfo<>(listall);
        pageInfo.setTotal(data1.size());
        return pageInfo;
    }

    /**
     * 获取所有除理论课之外的课程类型
     *
     * @return
     */
    private List<ChapterTypeEnum> getConfigTypes() {

        List<ChapterTypeEnum> types = Lists.newArrayList(ChapterTypeEnum.EXPERIMENT,
                ChapterTypeEnum.THEORY_EXPERIMENT, ChapterTypeEnum.THEORY_EXPERIMENT, ChapterTypeEnum.THEORY_MANY_EXPERIMENT);

        return types;
    }

    @Override
    public String experimentStatisticsVideoFind(Long chapterId, Long courseId, Long classId, Long userId, Long taskId, Long stepId) {
        Map<String, Object> queryExperimentByTask = mapper.findQueryExperimentByTask(courseId, chapterId, classId, userId, taskId, stepId);
        if (queryExperimentByTask != null) {
            String video_time = queryExperimentByTask.get("video_time") == null ? "" : queryExperimentByTask.get("video_time").toString();
            String video_sum_time = queryExperimentByTask.get("video_sum_time") == null ? "" : queryExperimentByTask.get("video_sum_time").toString();
            if (video_time.equals(video_sum_time)) {
                return "1";
            }
        }
        return "0";
    }

    @Override
    public void DeleteExperimentVideo(Long chapterId, Long courseId, Long taskId, Long stepId) {
        mapper.deleteExperimentVideo(courseId, chapterId, null, null, taskId, stepId);
    }

    @Override
    public void saveExperimentTime(Long chapterId, Long courseId, Long classId, Long userId, String videotime, Long taskId, Long stepId, String sumTime) {
        synchronized (ExperimentReportServiceImpl.class) {
            Map<String, Object> queryExperimentByTask = mapper.findQueryExperimentByTask(courseId, chapterId, classId, userId, taskId, stepId);

            if (queryExperimentByTask != null) {
                String sumtime = queryExperimentByTask.get("video_sum_time").toString();
                if (sumtime.equals(sumTime.toString())) {
                    String video_time = queryExperimentByTask.get("video_time").toString();
                    if (Double.valueOf(videotime) > Double.valueOf(video_time)) {
                        mapper.updateExperimentTimeVideo(courseId, chapterId, classId, userId, videotime, stepId, taskId);
                    }
                } else {
                    mapper.deleteExperimentVideo(courseId, chapterId, classId, userId, null, null);
                    mapper.insertExperimentTimeVideoOther(courseId, chapterId, classId, userId, videotime, stepId, taskId, sumTime.toString());
                }
            } else {
                mapper.insertExperimentTimeVideoOther(courseId, chapterId, classId, userId, videotime, stepId, taskId, sumTime.toString());
            }
        }

    }


    @Override
    public void download(List<ExperimentStatisticsParam> passStudent) {
        List<Map<String, Object>> excelList = new ArrayList<>();
        Map<String, Object> header = new HashMap<>();
        header.put("sheetName", "实验通过统计");
        excelList.add(header);
        for (ExperimentStatisticsParam quizStatisticVO : passStudent) {
            Map<String, Object> line = new HashMap<>();
            line.put("name", quizStatisticVO.getName());
            line.put("pass", quizStatisticVO.getPass());
            line.put("passed", quizStatisticVO.getPassed());
            List<String> strings = Arrays.asList(quizStatisticVO.getPassedName());
            String newStr = strings.stream().collect(Collectors.joining(","));
            line.put("passedName", newStr);
            excelList.add(line);
        }
        String[] columnNames = {"章节名称", "通过数", "未通过数", "未通过姓名"};
        String[] keys = {"name", "pass", "passed", "passedName"};
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        ExcelsUtil.ExcelSingleOutputStream(excelList, keys, columnNames, "pass", request, response);
    }

    @Override
    public void downloadAll(ExperimentStatisticsAllParam passStudent, String courseName, String chapterName, String className) {
        List<Map<String, Object>> excelList = new ArrayList<>();
        Map<String, Object> header = new HashMap<>();
        List list = Lists.newArrayList();
        header.put("sheetName", "实验统计");
        excelList.add(header);
        List<ExperimentStatisticsParam> pass = passStudent.getPass();
        if (pass.size() != 0) {
            String[] passedName = pass.get(0).getPassedName();
//            String[] split = passedName.split(",");
            for (String name : passedName) {
                list.add(name);
            }
        }
        List<ExperimentStatisticsTimeParam> studyAndVideoTime = passStudent.getStudyAndVideoTime();
        for (ExperimentStatisticsTimeParam quizStatisticVO : studyAndVideoTime) {
            Map<String, Object> line = new HashMap<>();
            line.put("name", quizStatisticVO.getUserName());
            line.put("userNum", quizStatisticVO.getUserNum());
            line.put("videoTime", quizStatisticVO.getStudyTime());
            line.put("studyTime", quizStatisticVO.getVideoTime());
            if (list.contains(quizStatisticVO.getUserName())) {
                line.put("pass", "未通过");
            } else {
                line.put("pass", "通过");
            }
            excelList.add(line);
        }
        String[] columnNames = {"学生姓名", "学生学号", "实验学习实验总时长", "实验学习视频总时长", "实验通过"};
        String[] keys = {"name", "userNum", "videoTime", "studyTime", "pass"};
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        ExcelsUtil.ExcelSingleOutputStreamExperiment(courseName, chapterName, className, excelList, keys, columnNames, "ExperimentStatistics", request, response);
    }


    @Override
    public ReportDTO selectInsertTest(Long answerSheetId, Long questionId, Long userId, Long courseId) {
        Long userClassId = userClassService.getUserClassId(courseId, userId);

        Long reportId = answerSheetExperimentService.getReportId(answerSheetId, questionId);
        // 实验报告为空时，异步插入一条新纪录
        if (reportId == null) {
            asyncInsertTest(answerSheetId, questionId, userClassId);
        }
        ExperimentReport report = reportId == null ? new ExperimentReport() : selectByPrimaryKey(reportId);

        ReportDTO result = BeanUtil.transform(ReportDTO.class, report);

        return transformModelInReport(result, courseId, TemplateTypeEnum.TEST_REPORT);
    }

    @Override
    public List<DuplicateResultDTO> getDuplicateResult(List<ExperimentReport> reportList, ExperimentReport studentReport, Integer reportDuplicatePercent) {
        List<DuplicateResultDTO> result = Lists.newArrayList();

        // 这段有问题
        List<Long> userClassIds = reportList.stream().map(ExperimentReport::getStudentId).collect(Collectors.toList());
        List<UserClass> userClassList = userClassService.selectByIds(userClassIds);

        Map<Long, Long> userClassIdUserIdMap = userClassList.stream().collect(Collectors.toMap(UserClass::getId, UserClass::getUserId));
        List<Long> userIds = userClassList.stream().map(UserClass::getUserId).collect(Collectors.toList());
        List<User> userList = userService.listUser(userIds);
        Map<Long, User> idUserMap = BeanUtil.mapByKey("id", userList);

        for (ExperimentReport report : reportList) {
            if (report.getReportFile() == null) continue;

            double percent = computeDuplicateResult(report.getReportFile(), studentReport.getReportFile());

            Long userId = userClassIdUserIdMap.get(report.getStudentId());
            User user = idUserMap.get(userId);

            if (percent >= reportDuplicatePercent && !Objects.equals(report.getId(), studentReport.getId())) {
                result.add(new DuplicateResultDTO(
                        report.getStudentId(), user.getUserNumber(), user.getName(), percent,
                        LocalResourcesEnum.REPORT.getMappingUrl(report.getReportFile()), report.getReportFileName()
                ));
            }
        }
        return result;
    }

    @Override
    public Long getCount(Long userId, ReportTypeEnum reportTypeEnum) {
        Integer type = reportTypeEnum.getType();
        return mapperExt.getCount(userId, type);
    }

    @Override
    public String uploadTestReport(Long answerSheetId, Long questionId, MultipartFile file, String username) {
        Long reportId = answerSheetExperimentService.getReportId(answerSheetId, questionId);

        CourseTestPaperQuestion question = questionService.selectQuestionById(questionId);

        if (file == null) {
            throw new GlobalException("请上传实验报告");
        }
        String fileActualName = file.getOriginalFilename();

        // 需要判断上传文件类型是否合规
        if (fileActualName.lastIndexOf(".") < 0) {
            throw new GlobalException("上传文件必须有后缀");
        }
        String extension = fileActualName.substring(fileActualName.lastIndexOf("."));
        /*if (isNotDoc(extension)) {
            throw new GlobalException("实验报告必须为word文件");
        }*/

        String fileName = String.format(ReportConstant.PAPER_REPORT_FORMAT_NAME, username, question.getOrderNumber(), extension);

        ReportNameDTO reportNameDTO = new ReportNameDTO(StringUtils.join(RandomUtil.simpleUUID(), extension), fileName);

        updateReport(reportId, reportNameDTO, null);

        asyncUpload(file, reportNameDTO.getFileServerName());

        return fileActualName;
    }

    private boolean isNotDoc(String extension) {
        return !".doc".equals(extension) && !".docx".equals(extension);
    }

    @Override
    public void saveTestReport(Long answerSheetId, Long questionId, ReportParam param) {
        Long reportId = answerSheetExperimentService.getReportId(answerSheetId, questionId);

        updateReport(reportId, null, param);
    }

    @Override
    public List<ChapterCountDTO> countGroupByChapterIdClassId(List<Long> chapterIds) {
        List<ChapterCountDTO> list = mapperExt.countGroupByChapterId(chapterIds);
        return CollectionUtils.isEmpty(list) ? Lists.newArrayList() : list;
    }

    private void updateReport(Long id, ReportNameDTO reportNameDTO, ReportParam param) {
        ExperimentReport report = new ExperimentReport();

        if (reportNameDTO != null) {
            report.setReportFile(reportNameDTO.getFileServerName());
            report.setReportFileName(reportNameDTO.getFileActualName());
        }

        if (param != null) {
            report.setReportHtml(param.getHtmlContent());
            report.setReportMd(param.getContent());
        }

        report.setId(id);

        mapper.updateByPrimaryKeySelective(report);
    }

    private void updateReport(Long chapterId, Long userId, ReportNameDTO reportName, ReportParam param) {
        ExperimentReport report = new ExperimentReport();
        if (reportName != null) {
            report.setReportFile(reportName.getFileServerName());
            report.setReportFileName(reportName.getFileActualName());
            report.setDocCommit(true);
        }
        if (param != null) {
            report.setReportHtml(param.getHtmlContent());
            report.setReportMd(param.getContent());
            report.setOnlineCommit(param.getFalg() == 1);
        }

        ExperimentReportExample example = new ExperimentReportExample();
        example.createCriteria().andChapterIdEqualTo(chapterId).andStudentIdEqualTo(userId).andDelMarkEqualTo(false);
        mapper.updateByExampleSelective(report, example);
    }

    private ExperimentReport selectByChapterIdUserId(Long chapterId, Long userClassId) {
        if (userClassId == null) {
            return new ExperimentReport();
        }
        ExperimentReportExample example = new ExperimentReportExample();
        example.createCriteria().andChapterIdEqualTo(chapterId).andStudentIdEqualTo(userClassId).andDelMarkEqualTo(false);
        List<ExperimentReport> list = mapper.selectByExampleWithBLOBs(example);
        return CollectionUtils.isEmpty(list) ? new ExperimentReport() : list.get(0);
    }

    @Async
    @Override
    public void asyncInsertTest(Long answerSheetId, Long questionId, Long userClassId) {
        ExperimentReport report = new ExperimentReport();
        report.setStudentId(userClassId);
        mapper.insertSelective(report);

        answerSheetExperimentService.insert(report.getId(), answerSheetId, questionId);
    }

    @Override
    public List<StudentBaseListDTO> listStudentBase(Long courseId, Long userId) {
        List<StudentBaseListDTO> list = mapperExt.listStudentBase(courseId, userId);
        return CollectionUtils.isEmpty(list) ? Lists.newArrayList() : list;
    }

    @Override
    public List<UserPaperExperimentReportDTO> selectByIdsExt(List<Long> reportIds) {
        if (CollectionUtils.isEmpty(reportIds)) {
            return Lists.newArrayList();
        }
        List<UserPaperExperimentReportDTO> result = mapperExt.selectByIds(reportIds);
        return CollectionUtils.isEmpty(result) ? Lists.newArrayList() : result;
    }

    @Override
    public Map<Long, List<DuplicateResultDTO>> mapQuestionDuplicate(CourseTestPaperAnswerSheet answerSheet, List<UserPaperExperimentReportDTO> experimentReportList, CourseTestPaper paper) {
        if (answerSheet == null || paper == null || !paper.getCheckReportDuplicate()) {
            return Maps.newHashMap();
        }

        Map<Long, List<DuplicateResultDTO>> result = Maps.newHashMap();

        List<UserPaperExperimentReportDTO> userReports = experimentReportList.stream()
                .filter(report -> Objects.equals(report.getAnswerSheetId(), answerSheet.getId()))
                .collect(Collectors.toList());
        userReports.forEach(userReport -> {
            List<UserPaperExperimentReportDTO> reports = experimentReportList.stream()
                    .filter(report -> Objects.equals(report.getQuestionId(), userReport.getQuestionId()))
                    .filter(report -> !Objects.equals(report.getAnswerSheetId(), userReport.getAnswerSheetId()))
                    .collect(Collectors.toList());
            result.put(userReport.getQuestionId(), getDuplicateResult(userReport, reports, paper.getReportDuplicatePercent()));
        });

        return result;
    }

    @Override
    public void updateTestCommit(Long answerSheetId) {
        mapperExt.updateByAnswerSheetId(answerSheetId);
    }

    @Async
    @Override
    public void updatePDF(String fileName, CheckSubmitParam param) {
        String filePath = LocalResourcesEnum.PDF.getLocalUploadPath(fileName);

        fileService.html2pdf(filePath, param.getHtmlStr());
    }

    @Override
    public List<ExperimentReport> selectByChapterIdsClassId(List<Long> chapterIds, Long classId) {
        List<ExperimentReport> list = mapperExt.selectByChapterIdsClassId(chapterIds, classId);
        return CollectionUtils.isEmpty(list) ? Lists.newArrayList() : list;
    }

    @Override
    public List<ClassUncheckedCountDTO> selectUncheckedByCourseIds(List<Long> courseIdList) {
        List<ClassUncheckedCountDTO> list = mapperExt.selectUncheckedByCourseIds(courseIdList);
        return CollectionUtils.isEmpty(list) ? Lists.newArrayList() : list;
    }

    @Override
    public List<ExperimentReport> selectByAnswerSheetIds(List<Long> answerSheetIds) {
        if (CollectionUtils.isEmpty(answerSheetIds)) {
            return Lists.newArrayList();
        }

        List<ExperimentReport> list = mapperExt.selectByAnswerSheetIds(answerSheetIds);
        return CollectionUtils.isEmpty(list) ? Lists.newArrayList() : list;
    }


    @Override
    public ExperimentChapterStatisticsDTO experimentChapterStatistics(Long taskId, Long classId) {

        //获取本课程下所有用户信息
        List<UserInfoDTO> userInfoDTOS = userService.queryClassUser(Lists.newArrayList(classId), null);
        // 获取本班级的所有学生ID
        List<UserClass> userClasses = userClassMapperExt.findNotDeleteUserClass(classId);

        // 实验统计时长查询
        ExperimentChapterStatisticsTimeDTO timeDTO = videoStatisticsService.loadExperimentTime(taskId, userInfoDTOS);
        // 报告信息统计
        ExperimentChapterStatisticsReportDTO reportDTO = loadExperimentReport(taskId, userInfoDTOS, userClasses);
        // 实验情况判定
        ExperimentChapterStatisticsPassDTO passDto = experimentStatisticsTaskPassService.loadExperimentPass(taskId, userInfoDTOS);
        // 返回装载
        ExperimentChapterStatisticsDTO countDto = new ExperimentChapterStatisticsDTO();
        countDto.setTimeDtO(timeDTO).setReportDto(reportDTO).setDecideDto(passDto);

        return countDto;
    }

    @Override
    public List<ChapterBasicInfoDTO> getChapterTaskId(Long chapterId, Long classId) {
        List<ChapterBasicInfoDTO> chapterBasicInfoDTOS = chapterService.listTask(Lists.newArrayList(chapterId), null);

        return chapterBasicInfoDTOS;
    }

    /**
     * 加载实验报告统计信息
     *
     * @param experimentChapterId
     * @param userInfoDTOS
     * @return
     */
    public ExperimentChapterStatisticsReportDTO loadExperimentReport(Long experimentChapterId, List<UserInfoDTO> userInfoDTOS, List<UserClass> userClasses) {

        ExperimentChapterStatisticsReportDTO reportDTO = new ExperimentChapterStatisticsReportDTO();

        if (CollectionUtil.isEmpty(userInfoDTOS)) {

            reportDTO.setAttendData(Lists.newArrayList());
            reportDTO.setAllUsers(0);
            reportDTO.setNotAttendData(Lists.newArrayList());
            // 保留两位小数
            reportDTO.setAverage("0");
            return reportDTO;
        }
        // 获取参加报告学生分数
        List<UserExperimentReportInfoDTO> attendData = mapperExt.getReportScoreCount(experimentChapterId, userClasses);
        // 获取平均分
        String scoreAverage = mapperExt.getReportScoreAverage(experimentChapterId, userClasses);
        // 获取未参与的学生信息
        List<UserExperimentReportInfoDTO> notAttendData = getNotAttendDataReport(userInfoDTOS, attendData);

        reportDTO.setAttendData(attendData);
        reportDTO.setAllUsers(userInfoDTOS.size());
        reportDTO.setNotAttendData(notAttendData);
        // 保留两位小数
        reportDTO.setAverage(NumberUtil.round(scoreAverage, 2).toString());

        return reportDTO;
    }

    /**
     * 获取未参加学生的信息
     *
     * @param userInfos
     * @param attendData
     * @return
     */
    private List<UserExperimentReportInfoDTO> getNotAttendDataReport(List<UserInfoDTO> userInfos, List<UserExperimentReportInfoDTO> attendData) {

        CopyOnWriteArrayList<UserInfoDTO> userInfosCopy = Lists.newCopyOnWriteArrayList(userInfos);

        outer:
        for (UserInfoDTO userInfoDTO : userInfosCopy) {
            for (UserExperimentReportInfoDTO attendUser : attendData) {
                if (userInfoDTO.getId().equals(attendUser.getUserId())) {
                    userInfosCopy.remove(userInfoDTO);
                    continue outer;
                }
            }
        }
        List<UserExperimentReportInfoDTO> notAttendDataReport = userInfosCopy.stream().map(user -> {
            UserExperimentReportInfoDTO userInfo = new UserExperimentReportInfoDTO();
            userInfo.setUserId(user.getId());
            userInfo.setUserName(user.getName());
            userInfo.setScore("0");
            return userInfo;
        }).collect(Collectors.toList());

        return notAttendDataReport;
    }


    private List<DuplicateResultDTO> getDuplicateResult(UserPaperExperimentReportDTO userReport, List<UserPaperExperimentReportDTO> reports, Integer reportDuplicatePercent) {
        List<DuplicateResultDTO> result = Lists.newArrayList();

        for (UserPaperExperimentReportDTO report : reports) {
            if (report.getReportFile() == null) continue;

            double percent = computeDuplicateResult(report.getReportFile(), userReport.getReportFile());

            if (percent >= reportDuplicatePercent && !Objects.equals(report.getId(), userReport.getId())) {
                result.add(new DuplicateResultDTO(
                        report.getStudentId(), report.getUserNumber(), report.getName(), percent,
                        LocalResourcesEnum.REPORT.getMappingUrl(report.getReportFile()), report.getReportFileName()
                ));
            }
        }

        if (CollectionUtils.isNotEmpty(result)) {
            result.sort(Comparator.comparing(DuplicateResultDTO::getPercent).reversed());
        }
        return result;
    }

    private Double computeDuplicateResult(String file1, String file2) {
        double percent = DocCheckUtil.similarity(
                LocalResourcesEnum.REPORT.getLocalUploadPath(file1),
                LocalResourcesEnum.REPORT.getLocalUploadPath(file2)
        ) * 100;

        BigDecimal bigDecimal = BigDecimal.valueOf(percent);
        return bigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    @Async
    void asyncInsert(Long chapterId, Long userClassId) {
        ExperimentReport report = new ExperimentReport();
        report.setChapterId(chapterId);
        report.setStudentId(userClassId);
        mapper.insertSelective(report);
    }

    @Async
    void asyncUpload(MultipartFile file, String fileName) {
        // 创建文件目录
        String dir = LocalResourcesEnum.REPORT.getLocalUploadPath();
        if (!new File(dir).exists()) {
            FileUtil.mkdir(dir);
        }

        String filePath = LocalResourcesEnum.REPORT.getLocalUploadPath(fileName);

        // 写入文件
        File newFile = new File(filePath);
        try {
            FileUtils.copyInputStreamToFile(file.getInputStream(), newFile);
        } catch (IOException e) {
            logger.error("异步写入文件异常", e);
        }
    }

    /**
     * 查询模板并根据result中值的存在情况进行封装
     *
     * @param result   待操作结果集
     * @param courseId 课程id
     * @return 结果集操作后返回结果集
     */
    private ReportDTO transformModelInReport(ReportDTO result, Long courseId, TemplateTypeEnum templateTypeEnum) {
        // 查询模板并根据result中值的存在情况进行封装
        try {
            CourseBasicDTO course = courseService.getCourse(courseId);
            TemplateDataDTO templateInfo = templateService.queryTemplate(course.getUserId(), templateTypeEnum);
            result = result.transform(templateInfo);
        } catch (NotFoundException e) {
            logger.info("未找到模板，直接返回实验报告结果");
        }

        if (result.getReportFile() != null) {
            result.setFilePath(LocalResourcesEnum.REPORT.getMappingUrl(result.getReportFile()));
        }

        return result;
    }

    @Override
    public List<ExperimentReport> findReportByUserIdsAndTaskIds(List<Long> userIds, List<Long> taskIds) {

        List<ExperimentReport> reports = mapperExt.findReportByUserIdsAndTaskIds(userIds, taskIds);
        Set<ExperimentReport> allUsers = new HashSet<>();
        allUsers.addAll(reports);
        // 2. 所有学生和所有任务数据
        for (Long userId : userIds) {
            for (Long taskId : taskIds) {
                ExperimentReport report = new ExperimentReport();
                report.setUserId(userId);
                report.setChapterId(taskId);
                allUsers.add(report);
            }
        }
        return new ArrayList<>(allUsers);
    }

    @Override
    public boolean chapterStatisticsDownLoad(Long chapterId, Long classId) {

        // 1. 查询实验类型
        ChapterBasic chapterInfo = chapterService.getChapterInfo(chapterId);
        Integer chapterInfoType = chapterInfo.getType();
        ChapterTypeEnum chapterTypeEnum = ChapterTypeEnum.getByCode(chapterInfoType);
        // 2. 查询当前班级下所有学员
        //通过工具类创建writer
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //获取本课程下所有用户信息
        List<UserInfoDTO> userInfoDTOS = userService.queryClassUser(Lists.newArrayList(classId), null);
        // 暂无学上信息
        if (CollectionUtil.isEmpty(userInfoDTOS)) {
            return false;
        }
        // 获取本班级的所有学生ID
        List<UserClass> studentInfos = userClassMapperHelper.findByCondition(classId);
        Chapter chapter = chapterMapperHelper.getChapterByCondition(chapterId);

        Long courseId = chapter.getCourseId();
        CourseBasicDTO course = courseService.getCourse(courseId);
        Class aClass = classService.queryClass(classId);

        String chapterName = chapterInfo.getName();
        String courseName = course.getName();
        String className = aClass.getName();
        String slogan = "";

        HSSFWorkbook workBook = null;
        // 3. 创建头
        // 4. 执行数据填充
        switch (chapterTypeEnum) {
            // 理论课
            case THEORY:
                slogan = "课程：" + courseName + "班级：" + className + "  章节：" + chapterName + " 学生" + ChapterTypeEnum.THEORY.getMessage() + "详情";
                workBook = theoryService.exportChapterTheory(chapterId, classId, userInfoDTOS, studentInfos, slogan);
                break;
            // 实验课
            case EXPERIMENT:
                slogan = "课程：" + courseName + "班级：" + className + "  章节：" + chapterName + " 学生" + ChapterTypeEnum.EXPERIMENT.getMessage() + "详情";
                workBook = exportChapterExperiment(chapterId, classId, userInfoDTOS, studentInfos, slogan, courseId);
                break;
            // 理论及单任务实验课
            case THEORY_EXPERIMENT:
                slogan = "课程：" + courseName + "班级：" + className + "  章节：" + chapterName + " 学生" + ChapterTypeEnum.THEORY_EXPERIMENT.getMessage() + "详情";
                workBook = exportChapterTheoryAndExperiment(chapterId, classId, userInfoDTOS, studentInfos, courseId, slogan);
                break;
            // 理论及多任务实验课
            case THEORY_MANY_EXPERIMENT:
                slogan = "课程：" + courseName + "班级：" + className + "  章节：" + chapterName + " 学生" + ChapterTypeEnum.THEORY_MANY_EXPERIMENT.getMessage() + "详情";
                workBook = exportChapterTheoryAndManyExperiment(chapterId, classId, userInfoDTOS, studentInfos, slogan, courseId);
                break;
        }
        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            workBook.write(os);
            StatisticExcelUtil.ExcelOutputStream("章节统计下载", request, response, os);
            workBook.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 导出多任务实验
     *
     * @param chapterId
     * @param classId
     * @param userInfoDTOS
     * @param studentInfos
     * @param slogan
     * @return
     */
    private HSSFWorkbook exportChapterTheoryAndManyExperiment(Long chapterId, Long classId,
                                                              List<UserInfoDTO> userInfoDTOS, List<UserClass> studentInfos, String slogan, Long courseId) {
        // 1. 根据章节ID获取理论课List
        HSSFWorkbook workbook = theoryService.exportChapterTheory(chapterId, classId, userInfoDTOS, studentInfos, slogan);
        CellStyle cellStyle = CellStyleUtils.createCellStyle(workbook);

        // 3.查询chapter下存在的task
        List<ChapterBasicInfoDTO> chapterBasicInfoDTOS = chapterService.listTask(Lists.newArrayList(chapterId), null);


        for (int i = 0; i < chapterBasicInfoDTOS.size(); i++) {

            String sheetName = "任务" + (i + 1);
            HSSFSheet sheet = chapterStatisticsExcelExportImpl.loadHeaderBySheet(ChapterTypeEnum.EXPERIMENT, slogan, workbook, sheetName);
            List<ExperimentChapterStatisticsExportDTO> lists = getExperimentChapterStatisticsExportDTOS(chapterBasicInfoDTOS.get(i).getId(), classId, userInfoDTOS, courseId);
            // 5. 填充数据 过滤学生Id
            chapterStatisticsExcelExportImpl.loadingData(sheet, lists, Lists.newArrayList("id"), cellStyle);
        }

        return workbook;
    }


    /**
     * 导出理论及实验
     *
     * @param chapterId
     * @param classId
     * @param userInfoDTOS
     * @param studentInfos
     * @param slogan
     * @return
     */
    private HSSFWorkbook exportChapterTheoryAndExperiment(Long chapterId, Long classId, List<UserInfoDTO> userInfoDTOS,
                                                          List<UserClass> studentInfos, Long courseId, String slogan) {

        // 1. 获取理论课的结果集
        List<TheoryChapterStatisticsExportDTO> theoryLists = theoryService.getTheoryChapterStatisticsExportDTOS(chapterId, userInfoDTOS, studentInfos);
        Map<Long, List<TheoryChapterStatisticsExportDTO>> theoryMap = theoryLists.stream().collect(Collectors.groupingBy(TheoryChapterStatisticsExportDTO::getUserId));
        // 2. 获取实验课的结果集
        List<ChapterBasicInfoDTO> tasks = chapterService.listTask(Lists.newArrayList(chapterId), null);
        if (CollectionUtil.isEmpty(tasks)) {
            throw new GlobalException("未查询到关联章节实验课");
        }
        List<ExperimentChapterStatisticsExportDTO> experimentLists = getExperimentChapterStatisticsExportDTOS(tasks.get(0).getId(), classId, userInfoDTOS, courseId);
        Map<Long, List<ExperimentChapterStatisticsExportDTO>> experimentUserMap = experimentLists.stream().collect(Collectors.groupingBy(ExperimentChapterStatisticsExportDTO::getId));
        // 3. 将结果集拼成实验及理论的结果集
        List<ToyExperimentChapterStatisticsExportDTO> toyExperimentChapterDTOS = BeanUtil.batchTransform(ToyExperimentChapterStatisticsExportDTO.class, userInfoDTOS);
        // 4. 将数据合并到结果集中
        for (ToyExperimentChapterStatisticsExportDTO toyExperimentChapterDTO : toyExperimentChapterDTOS) {

            Long userId = toyExperimentChapterDTO.getId();
            List<TheoryChapterStatisticsExportDTO> theorys = theoryMap.get(userId);
            List<ExperimentChapterStatisticsExportDTO> experiments = experimentUserMap.get(userId);

            // 理论部分填充
            if (CollectionUtil.isNotEmpty(theorys)) {
                TheoryChapterStatisticsExportDTO theory = theorys.get(0);
                toyExperimentChapterDTO.setVideoTime(theory.getVideoTime());
                toyExperimentChapterDTO.setLearnTime(theory.getLearnTime());
                toyExperimentChapterDTO.setScore(theory.getScore());
                toyExperimentChapterDTO.setExcellent(theory.getExcellent());
                toyExperimentChapterDTO.setGood(theory.getGood());
                toyExperimentChapterDTO.setPoor(theory.getPoor());
                toyExperimentChapterDTO.setCorrect(theory.getCorrect());
                toyExperimentChapterDTO.setInCorrect(theory.getInCorrect());
            }
            // 实验部分填充
            if (CollectionUtil.isNotEmpty(experiments)) {
                ExperimentChapterStatisticsExportDTO experiment = experiments.get(0);
                toyExperimentChapterDTO.setELearnTime(experiment.getLearnTime());
                toyExperimentChapterDTO.setEVideoTime(experiment.getVideoTime());
                toyExperimentChapterDTO.setEPassState(experiment.getPassState());
                toyExperimentChapterDTO.setEScore(experiment.getScore());
            }
        }
        // 5. 填充数据
        HSSFWorkbook workBook = chapterStatisticsExcelExportImpl.getWorkBook(ChapterTypeEnum.THEORY_EXPERIMENT, slogan);
        CellStyle cellStyle = CellStyleUtils.createCellStyle(workBook);
        HSSFSheet sheet = workBook.getSheetAt(0);
        chapterStatisticsExcelExportImpl.loadingData(sheet, toyExperimentChapterDTOS, Lists.newArrayList("id"), cellStyle);

        return workBook;
    }

    /**
     * 导出实验详情
     *
     * @param chapterId
     * @param classId
     * @param userInfoDTOS
     * @param studentInfos
     * @param slogan
     * @return
     */
    private HSSFWorkbook exportChapterExperiment(Long chapterId, Long classId, List<UserInfoDTO> userInfoDTOS,
                                                 List<UserClass> studentInfos, String slogan, Long courseId) {
        // 1. 查询章节下类型为实验的任务
        List<ChapterBasicInfoDTO> allTask = chapterService.listTask(Lists.newArrayList(chapterId), null);

        if (CollectionUtil.isEmpty(allTask)) {
            throw new GlobalException("未查询到当前章节下的实验课！");
        }
        List<ExperimentChapterStatisticsExportDTO> lists = getExperimentChapterStatisticsExportDTOS(allTask.get(0).getId(), classId, userInfoDTOS, courseId);
        // 5. 填充数据
        HSSFWorkbook workBook = chapterStatisticsExcelExportImpl.getWorkBook(ChapterTypeEnum.EXPERIMENT, slogan);
        CellStyle cellStyle = CellStyleUtils.createCellStyle(workBook);
        HSSFSheet sheet = workBook.getSheetAt(0);
        chapterStatisticsExcelExportImpl.loadingData(sheet, lists, Lists.newArrayList("id"), cellStyle);

        return workBook;
    }

    /**
     * 实验填充list
     *
     * @param taskId
     * @param classId
     * @param userInfoDTOS
     * @param courseId
     * @return
     */
    private List<ExperimentChapterStatisticsExportDTO> getExperimentChapterStatisticsExportDTOS(Long taskId, Long classId, List<UserInfoDTO> userInfoDTOS, Long courseId) {

        List<Integer> userIds = userInfoDTOS.stream().map(userInfoDTO -> userInfoDTO.getId().intValue()).collect(Collectors.toList());
        List<Long> lUserIds = userInfoDTOS.stream().map(UserInfoDTO::getId).collect(Collectors.toList());
        userInfoDTOS.stream().map(userInfoDTO -> userInfoDTO.getId().intValue()).collect(Collectors.toList());
        List<ExperimentChapterStatisticsExportDTO> lists = BeanUtil.batchTransform(ExperimentChapterStatisticsExportDTO.class, userInfoDTOS);
        // 实验视频时长
        Map<Long, List<ExperimentVideoStatistics>> videoMap = experimentVideoStatisticsMapperHelper.getByCondition(taskId, userIds)
                .stream().collect(Collectors.groupingBy(videoStatistics -> Long.valueOf(videoStatistics.getUserId())));
        // 实验学习时长
        List<Statistic> byUserIdsAndContext = statisticMapperHelper.getByUserIdsAndContext(lUserIds, ContextTypeEnum.TASK.getCode(), taskId);
        Map<Long, List<Statistic>> statisticMap = byUserIdsAndContext.stream().collect(Collectors.groupingBy(Statistic::getUserId));
        // 实验分数
        List<ExperimentReport> reportByUserIdsAndTaskIds = findReportByUserIdsAndTaskIds(lUserIds, Lists.newArrayList(taskId));
        Map<Long, List<ExperimentReport>> experimentReportMap = reportByUserIdsAndTaskIds.stream().collect(Collectors.groupingBy(ExperimentReport::getUserId));

        // 实验通过状态
        List<ExperimentStatisticsTaskPassDTO> experimentStatisticsTaskPassDTOs = experimentStatisticsTaskPassService.findExperimentByUserId(courseId, classId, Lists.newArrayList(taskId), lUserIds);
        Map<Long, List<ExperimentStatisticsTaskPassDTO>> experimentStatisticsTaskPassDTOMap = experimentStatisticsTaskPassDTOs.stream().collect(Collectors.groupingBy(ExperimentStatisticsTaskPassDTO::getUserId));

        for (ExperimentChapterStatisticsExportDTO experimentChapterStatisticsDTO : lists) {

            Long userId = experimentChapterStatisticsDTO.getId();
            // 存入默认值
            experimentChapterStatisticsDTO.initProperty();
            // 存入实验时长
            List<Statistic> statistics = statisticMap.get(userId);
            if (CollectionUtil.isNotEmpty(statistics)) {
                experimentChapterStatisticsDTO.setLearnTime(String.valueOf(statistics.get(0).getExperimentTime()));
            }
            // 存入学习时长
            List<ExperimentVideoStatistics> experimentVideoStatistics = videoMap.get(userId);
            if (CollectionUtil.isNotEmpty(experimentVideoStatistics)) {
                experimentChapterStatisticsDTO.setVideoTime(experimentVideoStatistics.get(0).getVideoTime());
            }

            // 存入实验分数
            List<ExperimentReport> reports = experimentReportMap.get(userId);
            if (CollectionUtil.isNotEmpty(reports)) {
                if (ObjectUtil.isNull(reports.get(0).getScore())) {
                    experimentChapterStatisticsDTO.setScore(0);
                } else {
                    experimentChapterStatisticsDTO.setScore(reports.get(0).getScore());
                }
            }
            // 存入实验通过状态
            experimentChapterStatisticsDTO.setPassState("否");
            List<ExperimentStatisticsTaskPassDTO> experimentStatisticsTaskPassDTOS = experimentStatisticsTaskPassDTOMap.get(userId);
            if (CollectionUtil.isNotEmpty(experimentStatisticsTaskPassDTOS)) {
                if (ObjectUtil.isNotNull(experimentStatisticsTaskPassDTOS.get(0).getPass())
                        && true == experimentStatisticsTaskPassDTOS.get(0).getPass()) {
                    experimentChapterStatisticsDTO.setPassState("是");
                }
            }
            // 将视频时常转换为小时
            experimentChapterStatisticsDTO.setTimeToHours();

        }
        return lists;
    }


    /**
     * 删除学生上传的记录 根据ID值
     *
     * @param id
     */
    @Override
    public void deleteReport(Long id) {

        ExperimentReport experimentReport = experimentReportMapperHelper.get(id);
        String reportPdf = experimentReport.getReportPdf();


    }


}
